//@ts-nocheck
import ReactCurrentOwner from './ReactCurrentOwner';
import {REACT_ELEMENT_TYPE} from '../shared/ReactSymbols';

function hasValidRef(config){
  return config.ref !== undefined;
}
function hasValidKey(config){
  return config.key !== undefined;
}

//RESERVED
//adj.	内向的；寡言少语的；矜持的
// v.	“reserve”的过去分词和过去式
// 网络	保留；保留的；预留
const RESERVED_PROPS = {
  key: true,
  ref: true,
  __self: true,
  __source: true
};

/** JSX会编译为createElement*/
export function createElement(type, config, children) {
  //变量声明 start ---
  /** 声明的这些变量都会作为ReactElement上的属性*/
  //#region
  const props = {}; // 就是 ReactElement 对象的 props属性
  let propName; // 属性名
  let key = null; // 兄弟节点中唯一标识自己, 在同一级不同兄弟之间的key要求不同, 用于domdiff

  /**
   ref可以接受三种形式的值
   + 字符串: <input ref="a"/>
   然后在组件内可通过this.refs.a获取到该ref

   + 回调函数: <input ref={(input:HTMLInputElement) => this.a = input}/>
   回调函数调用时机为: 该回调函数会在虚拟dom转换成真实dom以后 将真实dom作为参数传进来并调用
   然后在组件内可通过this.a获取到该ref

   + 对象: this.a = React.createRef() 或者 const refObject = useRef()
   <input ref={this.a}/> | <input ref={refObject}/>
   然后在组件内可通过 this.a.current | this.refObject.current 拿到对应的ref
   */
  let ref = null;

  /** ↓开发者可见,生产模式下不可见*/
  let self = null; // 用来获取真实的this指针
  let source = null; // 用来定位创建此虚拟DOM元素在源码的位置 哪个文件 哪一行  哪一列
  //#endregion
  //--- 变量声明 end


  //对要传递给ReactElement的属性进行预处理 start ---
  //#region
  if (config !== null) {
    if(hasValidRef(config)){
      ref = config.ref;
    }
    if(hasValidKey(config)){
      key = config.key;
    }

    //↓不是用户传的 需要我们自己维护
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;

    // 从config中筛选出要作为ReactElement的props的属性
    for(propName in config){
      if(!RESERVED_PROPS.hasOwnProperty(propName)){
        props[propName] = config[propName];
      }
    }
  }

  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children; // 如果说是独生子的话 children就是一个对象
  } else if (childrenLength > 1) {
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; ++i) {
      // eslint-disable-next-line prefer-rest-params
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray; // 如果说是有多个儿子的话 props.children就是一个数组
  }

  // 如果是类组件 静态属性defaultProps也会被加入到reactElement的props中
  if(type && type.defaultProps){
    const defaultProps = type.defaultProps;
    for(propName in defaultProps){
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  //#endregion
  //--- 对要传递给ReactElement的属性进行预处理 end

  // eslint-disable-next-line @typescript-eslint/no-use-before-define
  return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current/** ←哪个FiberNode创建出来的*/, props);
}

function ReactElement(type, key, ref, _self, _source, _owner, props) {
  const element = {

    // This tag allows us to uniquely identify this as a React Element
    // 这个标记允许我们唯一地将其标识为React元素
    // 就是个mark 表示这是react系统创建的元素节点
    $$typeof: REACT_ELEMENT_TYPE,

    type,
    key,
    ref,
    props,
    _owner, /** ←哪个FiberNode创建出来的*/
    _self,
    _source
  };
  return element;
}
